/*
===========================================================================
This source file is part of DyLab (Dynamic Laboratory)
For the latest info, see http://dylab.googlecode.com

Copyright (c) 2006-2008 Lukas Krejci
(krejci.lukas@volny.cz)

This file is part of DyLab.

    DyLab is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DyLab is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DyLab.  If not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

#ifndef __dylab_AssocObjContainerBase_h__
#define __dylab_AssocObjContainerBase_h__

//===========================================================================================
#include <dylab/utils/containers/ContainerBase.h>
#include <dylab/utils/containers/ObjManagerBase.h>
#include <dylab/utils/types/dynamicCast.h>

//===========================================================================================
namespace dylab {

    /** 
     */   
    template <
        class _STLcontainerT        
    >
    class AssocObjContainerBase 
        : public ContainerBase<_STLcontainerT>
        , public ObjManagerBase
    { 
    private:
        typedef AssocObjContainerBase<_STLcontainerT> AssocObjContainerBaseT;
        typedef typename _STLcontainerT::key_type KeyT;
        typedef typename _STLcontainerT::value_type::second_type ObjectPtrT;
    public:
        /// utility class (RIAA) for walking through all container elements
        class Iterator
            : public IteratorBase
        {
        public:
            Iterator(ContainerBaseT & _c)
                : IteratorBase(_c)
                    { }
            const KeyT & operator~ ()
                    { 
                        DYLAB_ASSERT(isValid());
                        return getCurrent()->first; 
                    }
            operator ObjectPtrT ()
                    { 
                        DYLAB_ASSERT(isValid());
                        return getCurrent()->second; 
                    }
            ObjectPtrT operator()()
                    { 
                        DYLAB_ASSERT(isValid());
                        return getCurrent()->second; 
                    }
            ObjectPtrT operator-> ()
                    { 
                        DYLAB_ASSERT(isValid());
                        return getCurrent()->second; 
                    }
        };
        //-----------------------------------------------------------------------------
    public:
        /// utility class (RIAA) for walking through all container elements
        class ConstIterator
            : public ConstIteratorBase
        {
        public:
            ConstIterator(const ContainerBaseT & _c)
                : ConstIteratorBase(_c)
                    { }
            const KeyT & operator~ ()
                    { 
                        DYLAB_ASSERT(isValid());
                        return getCurrent()->first; 
                    }
            operator const ObjectPtrT ()
                    { 
                        DYLAB_ASSERT(isValid());
                        return getCurrent()->second; 
                    }
            const ObjectPtrT operator()()
                    { 
                        DYLAB_ASSERT(isValid());
                        return getCurrent()->second; 
                    }
            const ObjectPtrT operator-> ()
                    { 
                        DYLAB_ASSERT(isValid());
                        return getCurrent()->second; 
                    }
        };
    public:
        //-----------------------------------------------------------------------------
        /// scoped reader class
        class Reader
            : public ReaderBase
        {
        public:
            Reader(const ContainerBaseT & _c)
                : ReaderBase(_c)
                    { }
            ObjectPtrT find(const KeyT & _key)
                    {
                        _STLcontainerT::const_iterator it = mSTLcontainer->find(_key);
                        return it != mSTLcontainer->end() ? it->second : NULL;
                    }
            const ObjectPtrT find(const KeyT & _key) const
                    {
                        _STLcontainerT::const_iterator it = mSTLcontainer->find(_key);
                        return it != mSTLcontainer->end() ? it->second : NULL; 
                    }
            bool has(const KeyT & _key) const
                    { 
                        return find(_key) != NULL; 
                    }
            ObjectPtrT get(const KeyT & _key)
                    { 
                        ObjectPtrT object = find(_key);
                        DYLAB_ASSERT_MSGF(object != NULL, "get: No object is associated with the key '%1%'.", _key);
                        return object;
                    }
            const ObjectPtrT get(const KeyT & _key) const
                    { 
                        const ObjectPtrT object = find(_key);
                        DYLAB_ASSERT_MSGF(object != NULL, "No object is associated with the key '%1%'.", _key);
                        return object;
                    }
        };
        //-----------------------------------------------------------------------------
    public:
        /// scoped writer class
        class Writer
            : public WriterBase
        {
        public:
            Writer(ContainerBaseT & _c)
                : WriterBase(_c)
                    { }
            void insert(const KeyT & _key, ObjectPtrT object)
                    {
                        DYLAB_ASSERT(object != NULL);
                        _STLcontainerT::iterator it = mSTLcontainer->find(_key);
                        DYLAB_ASSERT_MSGF(object != NULL, "insert: No object is associated with the key '%1%'.", _key);
                        mSTLcontainer->insert(_STLcontainerT::value_type(_key, object));
                    }
            ObjectPtrT remove(const KeyT & _key)
                    { 
                        _STLcontainerT::iterator it = mSTLcontainer->find(_key);
                        DYLAB_ASSERT_MSGF(it != mSTLcontainer->end(), "remove: No object is associated with the key '%1%'.", _key);
                        if (dynamicCast<AssocObjContainerBaseT>(mContainerBase)->isObjectOwner())
						{
                            delete it->second;
							mSTLcontainer->erase(it);
							return NULL;
						}
						else
						{
							ObjectPtrT removedObj = it->second;
							mSTLcontainer->erase(it);
							return removedObj;
						}
                    }
            void removeAll()
                    {
                        if (dynamicCast<AssocObjContainerBaseT>(mContainerBase)->isObjectOwner())
                            for (_STLcontainerT::iterator it = mSTLcontainer->begin(); it != mSTLcontainer->end(); it++)
                                delete it->second;
                        mSTLcontainer->clear();
                    }
        };
        //-----------------------------------------------------------------------------
    public:

        AssocObjContainerBase(bool _objectOwner)
            : ObjManagerBase(_objectOwner)
                { }

        virtual ~AssocObjContainerBase()
                { removeAll(); }       

        ObjectPtrT find(const KeyT & _key)
                { return Reader(*this).find(_key); }
        const ObjectPtrT find(const KeyT & _key) const
                { return Reader(*this).find(_key); }
        bool has(const KeyT & _key) const
                { return Reader(*this).has(_key); }
        ObjectPtrT get(const KeyT & _key)
                { return Reader(*this).get(_key); }
        const ObjectPtrT get(const KeyT & _key) const
                { return Reader(*this).get(_key); }

        /**
         * Inserts the pair <_key,object> into the container.
         */
        void insert(const KeyT & _key, ObjectPtrT object)
                { Writer(*this).insert(_key, object); }

        /**
         * Remove the element associated with the _key.
         * The element has to exists in the container.
         */
        ObjectPtrT remove(const KeyT & _key)
                { return Writer(*this).remove(_key); }

        /**
         * Removes (and if _ObjectOwner set then deletes) all container elements.
         */
        void removeAll()
                { Writer(*this).removeAll(); }
    };    
}
//===========================================================================================
#endif // __dylab_AssocObjContainerBase_h__
